package com.nowcoder.community.controller;

import com.google.code.kaptcha.Producer;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.UserService;
import com.nowcoder.community.utils.CommunityUtils;
import com.nowcoder.community.utils.MailClient;
import com.nowcoder.community.utils.RedisKeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.nowcoder.community.CommunityConstants.*;

@Controller
@Slf4j
public class LoginController {

    @Autowired
    private UserService userService;

    @Autowired
    private Producer kaptchaProducer;

    @Autowired
    private MailClient mailClient;

    @Autowired
    private TemplateEngine templateEngine;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${server.servlet.context-path}")
    private String contextPath;

    /*
     * 获取 注册页面
     * */
    @GetMapping("/register")
    public String getRegisterPage(){
        return "/site/register";
    }

    /*
     * 获取 登录页面
     * */
    @GetMapping("/login")
    public String getLoginPage(){
        return "/site/login";
    }

    /*
     * 处理 注册表单
     * */
    @PostMapping("/register")
    public String userRegister(Model model, User user){
        Map<String, Object> map = userService.registerUser(user);
        // 注册成功并跳转到首页
        if (map == null || map.isEmpty()){
            model.addAttribute("msg", "已向您的邮箱发送激活邮件");
            model.addAttribute("url", "/index");
            return "/site/operate-result.html";
        }else {
            // 注册失败
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            model.addAttribute("emailMsg", map.get("emailMsg"));
            return "/site/register";
        }
    }

    /*
     * 激活用户
     * */
    @GetMapping("/activation/{userId}/{activationCode}")
    public String activateUser(
            Model model,
            @PathVariable("userId") int userId,
            @PathVariable("activationCode") String activationCode
    ){
        ActivationResult result = userService.activateUser(userId, activationCode);
        if (result == ActivationResult.SUCCESS){
            model.addAttribute("msg", "激活成功");
            model.addAttribute("url", "/login");
        }else if(result == ActivationResult.NO_SUCH_USER){
            model.addAttribute("msg", "激活失败，用户不存在");
            model.addAttribute("url", "/index");
        }else if (result == ActivationResult.REPATED){
            model.addAttribute("msg", "激活失败，重复激活");
            model.addAttribute("url", "/index");
        }else if (result == ActivationResult.INVALID_CODE){
            model.addAttribute("msg", "激活失败，激活码无效");
            model.addAttribute("url", "/index");
        }else{
            model.addAttribute("msg", "激活失败，未知错误");
            model.addAttribute("url", "/index");
        }
        return "/site/operate-result.html";
    }

    /*
     * 获取验证码图片
     * */
    @GetMapping("/kaptcha")
    public void getKaptcha(HttpServletResponse response){
        // 生成验证码
        String text = kaptchaProducer.createText();
        BufferedImage image = kaptchaProducer.createImage(text);

        // 将验证码保存到 redis
        String kaptchaOwner = CommunityUtils.generateUUID();
        Cookie cookie = new Cookie("kaptchaOwner", kaptchaOwner);
        cookie.setPath(contextPath);
        cookie.setMaxAge(60);
        response.addCookie(cookie);
        String kaptchaOwnerRedisKey = RedisKeyUtil.getKaptchaOwnerRedisKey(kaptchaOwner);
        redisTemplate.opsForValue().set(kaptchaOwnerRedisKey, text, 60, TimeUnit.SECONDS);

        // 返回验证码图片
        try {
            response.setContentType("image/png");
            ServletOutputStream outputStream = response.getOutputStream();
            ImageIO.write(image, "png", outputStream);
        } catch (IOException e) {
            log.error("验证码字节流写入响应体失败");
        }
    }

    /*
     * 处理 登录表单
     * */
    @PostMapping("/login")
    public String login(String username, String password, String code, boolean rememberMe,
                        @CookieValue("kaptchaOwner") String kaptchaOwner,
                        Model model, HttpServletResponse response){

        // 校验验证码
        String kaptcha = null;
        if (!StringUtils.isBlank(kaptchaOwner)) {
            kaptcha = (String) redisTemplate.opsForValue().get(RedisKeyUtil.getKaptchaOwnerRedisKey(kaptchaOwner));
        }

        if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !code.equalsIgnoreCase(kaptcha)){
            model.addAttribute("codeMsg", "验证码不正确");
            return "/site/login";
        }

        // 校验账号
        int day = 60 * 60 * 24;
        int expiredSeconds = rememberMe ? day * 7 : day;
        Map<String, Object> map = userService.loginUser(username, password, expiredSeconds);
        if (map.containsKey("ticket")){
            // 账号校验成功
            Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
            cookie.setPath(contextPath);
            cookie.setMaxAge(expiredSeconds);
            response.addCookie(cookie);
            return "redirect:/index";
        }else {
            // 账号校验失败
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            return "/site/login";
        }
    }

    /*
     * 退出登录
     * */
    @GetMapping("/logout")
    public String logout(@CookieValue("ticket") String ticket){
        userService.logoutUser(ticket);
        SecurityContextHolder.clearContext();
        return "redirect:/login";
    }

    // 忘记密码
    // 获取忘记密码页面
    @GetMapping("/forget")
    public String getForgetPage(){
        return "/site/forget";
    }

    // 获取修改密码的验证码
    @PostMapping("/forget/code")
    @ResponseBody
    public Map<String, Object> sendForgetCodeToEmail(String email, HttpSession session){
        Map<String, Object> map = new HashMap<>();
        if (!userService.isEmailExists(email)){
            map.put("emailMsg", "您输入的邮箱不存在");
            return map;
        }

        String forgetCode = CommunityUtils.generateUUID().substring(0, 6);
        session.setAttribute("forgetCode", forgetCode);
        session.setAttribute("email", email);

        Context context = new Context();
        context.setVariable("email", email);
        context.setVariable("forgetCode",forgetCode);
        String content = templateEngine.process("/mail/forget", context);
        mailClient.send(email, "修改密码", content);
        map.put("codeMsg", "已向您的邮箱发送验证码");
        return map;
    }

    // 处理忘记密码表单
    @PostMapping("/forget")
    public String forget(String email, String code, String password,
                         Model model, HttpSession session){

        Object e = session.getAttribute("email");
        if (e == null || StringUtils.isBlank(email) || !email.equals(e.toString())){
            model.addAttribute("emailMsg", "邮箱不正确");
            return "/site/forget";
        }

        Object c = session.getAttribute("forgetCode");
        if (c == null || StringUtils.isBlank(code) || !code.equalsIgnoreCase(c.toString())){
            model.addAttribute("codeMsg", "验证码不正确");
            return "/site/forget";
        }

        userService.updatePassword(email, password);
        return "redirect:/login";
    }

}
